


#include <stdio.h>
#include <unistd.h>
#include "vs-crypto.h"



#define VSCRYPTO_LOGNAME    "vscryp"
#define vsc_debug(...)   qelog_debug(VSCRYPTO_LOGNAME,   __VA_ARGS__)
#define vsc_info(...)    qelog_info(VSCRYPTO_LOGNAME,    __VA_ARGS__)
#define vsc_notice(...)  qelog_notice(VSCRYPTO_LOGNAME,  __VA_ARGS__)
#define vsc_warning(...) qelog_warn(VSCRYPTO_LOGNAME, __VA_ARGS__)
#define vsc_error(...)   qelog_error(VSCRYPTO_LOGNAME,   __VA_ARGS__)



qe_ret vscrypto_top_header_file_append(const char *file, vscrypo_top_header_t *thdr)
{
    int read_bytes;
    FILE *fp, *tmp;
    char *buf = QE_NULL;
    char tmpname[256] = {0};
    time_t now;

    buf = qe_malloc(4096);
    if (!buf) {
        vsc_error("create gbuf error");
        return qe_err_oom;
    }

    fp = fopen(file, "rb");
    if (!fp) {
        vsc_error("open file %s error", file);
        return qe_err_io;
    }

    sprintf(tmpname, sizeof(tmpname), "%s.tmp", file);

    tmp = fopen(tmpname, "wb");
    if (!tmp) {
        vsc_error("open tmpfile error");
        fclose(fp);
        return qe_err_io;
    }

    /* write header to tmpfile */
    time(&now);
    thdr.magic = VSCRYPTO_TOP_MAGIC;
    thdr.create_timestamp = (qe_u32)now;
    fwrite(thdr, sizeof(vscrypo_top_header_t), 1, tmp);

    /* write origin file data to tmpfile */
    while ((read_bytes = fread(buf, 1, 4096, fp)) > 0) {
        fwrite(buf, 1, read_bytes, tmp);
    }

    /* close origin and tmp file */
    fclose(tmp);
    fclose(fp);

    /* delete origin file */
    if (remove(file) != 0) {
        vsc_error("delete original file %s error", file);
        return qe_err_io;
    }

    /* rename the tmpfile to origin name */
    if (rename(tmp, file) != 0) {
        vsc_error("rename %s error", tmpname);
        return 1;
    }

    vsc_info("append tophdr to %s", file);
    return qe_ok;
}

qe_ret vscrypto_init(vscrypto_ctx_t *ctx)
{
    qe_ret ret;

    if (ctx->mode == VSCRYPTO_M_ENC) {
        if (!ctx->aes_key_create ||
            !ctx->aes_vec_create ||
            !ctx->aes_encrypt) {
            vsc_error("Encrypt handle not set");
            return qe_err_param;
        }
    }

    if (!ctx->rsa_pub_key_create) {
        vsc_error("RSA public key create handle not set");
        return qe_err_param;
    } else {
        ret = ctx->rsa_pub_key_create(ctx);
        if (ret != qe_ok) {
            vsc_error("RSA public key create error:%d", ret);
            return ret;
        }
    }

    if (ctx->mode & VSCRYPTO_M_DEC) {
        if (!ctx->rsa_pri_key_create ||
            !ctx->rsa_decrypt ||
            !ctx->aes_decrypt) {
            vsc_error("RSA private key create handle not set");
            return qe_err_param;
        } else {
            ret = ctx->rsa_pri_key_create(ctx);
            if (ret != qe_ok) {
                vsc_error("RSA private key create error:%d", ret);
                return ret;
            }
        }
    }

    ctx->initialized = 1;

    return qe_ok;
}

static int aes_bits2mode(int bits)
{
    switch (bits) {
    case 128: return VSCRYPTO_AES_BITS_128;
    case 192: return VSCRYPTO_AES_BITS_192;
    case 256: return VSCRYPTO_AES_BITS_256;
    default: return -1;
    }
}

static int aes_mode2bits(int mode)
{
    switch (mode) {
    case VSCRYPTO_AES_BITS_128: return 128;
    case VSCRYPTO_AES_BITS_192: return 192;
    case VSCRYPTO_AES_BITS_256: return 256;
    default: return -1;
    }
}

int vscrypto_align(vscrypto_ctx_t *ctx, int size)
{
    return (size+16) & (~16);
}

qe_ret vscrypto_encrypt(vscrypto_ctx_t *ctx, qe_u8 *in, int in_size, qe_u8 *out, int out_size, 
    vscrypto_batch_header_t *head)
{
    qe_ret ret;
    qe_u8 vec[16];
    qe_u8 key[32];
    qe_u8 key_enc[32];

    if (!ctx->rsa_encrypt || !ctx->aes_encrypt || !ctx->aes_key_create) {
        vsc_error("callback null");
        return qe_err_param;
    }

    if (!ctx || !in || !out || size<=0) {
        vsc_error("param error");
        return qe_err_param;
    }

    ret = ctx->aes_key_create(key, ctx->aes_bits);
    if (ret != qe_ok) {
        vsc_error("AES key create error:%d", ret);
        return qe_err_param;
    }

    ret = ctx->aes_key_create(vec);
    if (ret != qe_ok) {
        vsc_error("AES vec create error:%d", ret);
        return qe_err_param;
    }

    ret = ctx->rsa_encrypt(key, ctx->aes_bits/8, key_enc);
    if (ret != qe_ok) {
        vsc_error("AES key encrypt error:%d", ret);
        return qe_err_common;
    }

    ret = ctx->aes_encrypt(ctx, key, vec, in, size, out);
    if (ret != qe_ok) {
        vsc_error("Video encrypt error:%d", ret);
        return qe_err_common;
    }

    head->magic = VSCRYPTO_TOP_MAGIC;
    head->multi_frames = 0;
    head->frames = 1;
    head->length = out_size;
    head->chk_mode = VSCRYPTO_CHK_CRC32;
    head->aes_mode = ctx->aes_mode;
    head->aes_padding = ctx->aes_padding;
    head->aes_bits = aes_bits2mode(ctx->aes_bits);
    head->plain_len = in_size;
    memcpy(head->aes_key, key_enc, ctx->aes_bits/8);
    memcpy(head->aes_vec, vec, 16);

    return qe_ok;
}

qe_ret vscrypto_file_decrypt(vscrypto_dec_ctx_t *ctx, const char *input, const char *output)
{
    qe_ret ret;
    qe_u8 key[32];
    qe_u8 vec[32];
    qe_u32 checksum;
    int bits;
    qe_u8 *enc_buf, *dec_buf;
    FILE *fp_input, *fp_output;
    vscrypto_batch_header_t head;

    if (vscrypto_dec_init(ctx) != qe_ok) {
        vsc_error("intt error:%d");
        return -1;
    }

    fp_input = fopen(input, "rb");
    if (!fp_input) {
        vsc_error("Open file %s error", input);
        ret = qe_err_io;
        goto __exit;
    }

    fp_output = fopen(output, "w");
    if (!fp_output) {
        vsc_error("Open file %s error", output);
        ret = qe_err_io;
        goto __exit;
    }

    fread(&head, sizeof(head), 1, fp_input);

    fseek(fp_input, 0, SEEK_SET);

    if (head.magic != VSCRYPTO_TOP_MAGIC) {
        vsc_error("File head magic not match 0x%x", head.magic);
        ret = qe_err_match;
        goto __exit;
    }

    while (!feof(fp_input)) {

        fread(&head, sizeof(head), 1, fp_input);
        if (head.magic != VSCRYPTO_TOP_MAGIC) {
            vsc_error("File head magic not match 0x%x", head.magic);
            ret = qe_err_match;
            goto __exit;
        }

        enc_buf = qe_malloc(head.length);
        if (!enc_buf) {
            vsc_error("Malloc enc buf error");
            ret = qe_err_oom;
            goto __exit;
        }

        dec_buf = qe_malloc(head.length);
        if (!dec_buf) {
            vsc_error("Malloc dec buf error");
            ret = qe_err_oom;
            goto __exit;
        }

        fread(enc_buf, head.length, 1, fp_input);

        bits = aes_mode2bits(head.aes_bits);
        ret = ctx->rsa_decrypt(head.key, bits/8, key);
        if (ret != qe_ok) {
            vsc_error("RSA decrypt aes key error:%d", ret);
            goto __exit;
        }

        ret = ctx->aes_decrypt(ctx, key, head.vec, enc_buf, head.length, dec_buf);
        if (ret != qe_ok) {
            vsc_error("AES decrypt data error:%d", ret);
            goto __exit;
        }

        fread(&checksum, sizeof(checksum), 1, fp_input);

        fwrite(dec_buf, head.plain_len, 1, fp_output);
    }

__exit:
    if (enc_buf)
        qe_free(enc_buf);
    if (dec_buf)
        qe_free(dec_buf);
    if (fp_input)
        fclose(fp_input);
    if (fp_output)
        fclose(fp_output);
    return ret;
}

int main(int argc, char *argv[])
{
    vscrypto_ctx_t ctx;

    ctx.mode = VSCRYPTO_M_ENC;
    ctx.rsa_pub_key_create = xxx_rsa_pub_key;
    ctx.rsa_encrypt = xxx_rsa_encrypt;
    ctx.aes_key_create = xxx_aes_key_create;
    ctx.aes_vec_create = xxx_aes_vec_create;
    ctx.aes_bits = 128;
    ctx.aes_mode = VSCRYPTO_CRY_AES_CBC;
    ctx.aes_padding = VSCRYPTO_AES_PAD_PKC7;

    if (vscrypto_init(&ctx) != qe_ok) {
        vsc_error("intt error:%d");
        return -1;
    }

    while (new frame came) {
        vscrypto_package_t pkg;
        size = vscrypto_align(frame.size);
        buffer = malloc(size);
        vscrypto_encrypt(&ctx, frame.data, frame.size, buffer, size, &pkg);
        fwrite(&pkg.head, sizeof(pkg.head), 1, fp);
        fwrite(buffer, size, 1, fp);
        fwrite(&pkg.checksum, sizeof(pkg.checksum), 1, fp);
        free(buffer);
    }

    vscrypto_deinit(&ctx);

    vscrypto_dec_ctx_t dec_ctx;
    dec_ctx.rsa_pub_key_create = xxx_rsa_pub_key;
    dec_ctx.rsa_pri_key_create = xxx_rsa_pri_key;
    dec_ctx.rsa_decrypt = xxx_rsa_decrypt;
    dec_ctx.aes_decrypt = xxx_aes_decrypt;
    vscrypto_file_decrypt(&ctx, "test", "test-out");
}